#include "project/initialization.h"

PlyExtremityVertices *
getPlyExtremityVertices(int num_vertices, Vertex** vlist)
{
	register int i;
	PlyExtremityVertices* ext_vertices = (PlyExtremityVertices *) calloc(1, sizeof(PlyExtremityVertices));

	if (!ext_vertices)
	{
		fprintf(stderr, "getPlyExtremityVertices: error allocating memory for extremity vertices structure\n");
		exit(-1);
	}  

	for (i = 0; i < num_vertices; i++)
	{
		if ( vlist[i]->x < vlist[ext_vertices->x_ext_min]->x )
			ext_vertices->x_ext_min = i;
                
		if ( vlist[i]->y < vlist[ext_vertices->y_ext_min]->y )
			ext_vertices->y_ext_min = i;
                   
		if ( vlist[i]->z < vlist[ext_vertices->z_ext_min]->z )
			ext_vertices->z_ext_min = i;
                   
		if ( vlist[i]->x > vlist[ext_vertices->x_ext_max]->x )
			ext_vertices->x_ext_max = i;
                
		if ( vlist[i]->y > vlist[ext_vertices->y_ext_max]->y )
			ext_vertices->y_ext_max = i;
                   
		if ( vlist[i]->z > vlist[ext_vertices->z_ext_max]->z )
			ext_vertices->z_ext_max = i;

	}
	return ext_vertices;         
}

PlyBarycenterCoords *
getPlyBarycenterCoords(int num_vertices, Vertex** vlist)
{
	register int i;
	PlyBarycenterCoords* barycenter_coords = (PlyBarycenterCoords *) calloc (1, sizeof(PlyBarycenterCoords));

	if (!barycenter_coords)
	{
		fprintf(stderr, "getPlyBarycenterCoords: error allocating memory for barycenter coords structure\n");
		exit(-1);
	}  

	for (i = 0; i < num_vertices; i++)
	{
		barycenter_coords->xm += vlist[i]->x;
		barycenter_coords->ym += vlist[i]->y;
		barycenter_coords->zm += vlist[i]->z;
	}
	barycenter_coords->xm /= i;
	barycenter_coords->ym /= i;
	barycenter_coords->zm /= i;

	return barycenter_coords;	
}

PlyBoundingSphere *
getPlyBoundingSphere(int num_vertices, Vertex** vlist, PlyBarycenterCoords* bcoords)
{
	register int i;
	float temp_radius;

	PlyBoundingSphere* bounding_sphere = (PlyBoundingSphere *) calloc (1, sizeof(PlyBoundingSphere));

	if (!bounding_sphere)
	{
		fprintf(stderr, "getPlyBoundingSphere: error allocating memory for bounding sphere structure\n");
		exit(-1);
	}  

	bounding_sphere->x = bcoords->xm;
	bounding_sphere->y = bcoords->ym;
	bounding_sphere->z = bcoords->zm;

	for (i = 0; i < num_vertices; i++)
	{
		/* Raio = sqrtf( powf( (x - x0), 2) + powf( (y - y0), 2) + powf( (z - z0), 2) */
		if ( (temp_radius = sqrtf( 
			powf( (vlist[i]->x - bounding_sphere->x), 2 ) +
			powf( (vlist[i]->y - bounding_sphere->y), 2 ) +
			powf( (vlist[i]->z - bounding_sphere->z), 2 )
			)) > bounding_sphere->radius)
		{
			bounding_sphere->radius = temp_radius;
		}
	}

	return bounding_sphere;
}

PlyTranslationOffset *
getPlyTranslationOffset()
{
	PlyTranslationOffset* tr_offset = (PlyTranslationOffset *) calloc (1, sizeof(PlyTranslationOffset));

	if (!tr_offset)
	{
		fprintf(stderr, "getPlyTranslationOffset: error allocating memory for translation offset structure\n");
		exit(-1);
	}
	return tr_offset;  
}

PlyRotationOffset *
getPlyRotationOffset()
{
	PlyRotationOffset* rt_offset = (PlyRotationOffset *) calloc (1, sizeof(PlyRotationOffset));

	if (!rt_offset)
	{
		fprintf(stderr, "getPlyRotationOffset: error allocating memory for rotation offset structure\n");
		exit(-1);
	}
	return rt_offset;  
}

void
setPlyModelID(PlyModel* ply_model)
{
	ply_model->id = next_ply_model_id;

	next_ply_model_id++;
}

void
setPlyModelAspect(PlyModel* ply_model)
{
	float width = 1.0f;
	float height = 1.0f;

	width = ply_model->vlist[ ply_model->ext_vertices->x_ext_max ]->x - ply_model->vlist[ ply_model->ext_vertices->x_ext_min ]->x;
	height = ply_model->vlist[ ply_model->ext_vertices->y_ext_max ]->y - ply_model->vlist[ ply_model->ext_vertices->y_ext_min ]->y;

	ply_model->aspect = width / height;
}

void
setPlyModelIdealEyeZ(PlyModel* ply_model)
{
	/* Calcula o melhor valor da coordenada Z da camera dado um angulo do campo de visao vertical de 45 graus */
	/* Obs.: eyeZ = raio * 2^0.5 (para nao haver 'clipping') */
	ply_model->ideal_eyeZ = ply_model->b_sphere->radius * powf(2.0f, 0.5f);
}

PlyModel*
initPly(char* scene_name, char* file_name)
{
	PlyModel* ply_model = (PlyModel *) calloc (1, sizeof(PlyModel));

	if (!ply_model)
	{
		fprintf(stderr, "loadPly: error allocating memory for ply model structure\n");
		exit(-1);
	}  

	loadPly(file_name, &ply_model->num_vertices, &ply_model->num_faces, &ply_model->vlist, &ply_model->flist);

	ply_model->file_name = file_name;
	ply_model->scene_name = scene_name;

	ply_model->bc_coords = getPlyBarycenterCoords( ply_model->num_vertices, ply_model->vlist );
	ply_model->ext_vertices = getPlyExtremityVertices( ply_model->num_vertices, ply_model->vlist );
	ply_model->b_sphere = getPlyBoundingSphere( ply_model->num_vertices, ply_model->vlist, ply_model->bc_coords);
	ply_model->tr_offset = getPlyTranslationOffset();
	ply_model->rt_offset = getPlyRotationOffset();

	setPlyModelID(ply_model);
	setPlyModelAspect(ply_model);
	setPlyModelIdealEyeZ(ply_model);

	showPlyInfo(ply_model);

	return ply_model;	
}

void
showPlyInfo(PlyModel* ply_model)
{
	/* Mostra (no terminal) informacoes sobre modelos carregados */
	printf("\n");
	printf("                  Nome do arquivo PLY: %s\n", ply_model->file_name);
	printf("           Nome do objeto PLY na cena: %s\n", ply_model->scene_name);
	printf("             ID do objeto PLY na cena: %d\n", ply_model->id);
	printf("                   Numero de vertices: %d\n", ply_model->num_vertices);
	printf("                      Numero de faces: %d\n", ply_model->num_faces);
	printf("          Raio da esfera circunscrita: %f\n", ply_model->b_sphere->radius);

	printf("                                 xmin: %.6f\n", ply_model->vlist[ply_model->ext_vertices->x_ext_min]->x);
	printf("                                 xmax: %.6f\n", ply_model->vlist[ply_model->ext_vertices->x_ext_max]->x);
	printf("                                 ymin: %.6f\n", ply_model->vlist[ply_model->ext_vertices->y_ext_min]->y);
	printf("                                 ymax: %.6f\n", ply_model->vlist[ply_model->ext_vertices->y_ext_max]->y);
	printf("                                 zmin: %.6f\n", ply_model->vlist[ply_model->ext_vertices->z_ext_min]->z);
	printf("                                 zmax: %.6f\n", ply_model->vlist[ply_model->ext_vertices->z_ext_max]->z);

	printf("                      X do Baricentro: %f\n", ply_model->bc_coords->xm);
	printf("                      Y do Baricentro: %f\n", ply_model->bc_coords->ym);
	printf("                      Z do Baricentro: %f\n", ply_model->bc_coords->zm);

	printf("Razao LARGURA / ALTURA (aspect ratio): %f\n", ply_model->aspect);
	printf("                Valor ideal para eyeZ: %f\n", ply_model->ideal_eyeZ);
	printf("\n");
}

